﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace ThreadConsole
{
    /// <summary>
    /// <example>
    /// using (ProducerConsumerQueue q = new ProducerConsumerQueue())
    /// {
    ///   q.EnqueueTask ("Hello");
    ///   for (int i = 0; i < 10; i++) q.EnqueueTask ("Say " + i);
    ///   q.EnqueueTask ("Goodbye!");
    /// }
    /// Exiting the using statement calls q's Dispose method, which enqueues a null task and waits until the consumer finishes.
    /// </example>
    /// </summary>
    public class ProducerConsumerQueue : IDisposable
    {
        EventWaitHandle _wh = new AutoResetEvent(false);
        Thread _worker;
        readonly object _locker = new object();
        Queue<string> _tasks = new Queue<string>();

        public ProducerConsumerQueue()
        {
            _worker = new Thread(Work);
            _worker.Start();
        }

        public void EnqueueTask(string task)
        {
            lock (_locker)
            {
                _tasks.Enqueue(task);
            }
            _wh.Set();
        }

        private void Work()
        {
            while (true)
            {
                string task = null;
                lock (_locker)
                {
                    if (_tasks.Count > 0)
                    {
                        task = _tasks.Dequeue();
                        if (null == task) return;
                    }
                }
                if (task != null)
                {
                    Console.WriteLine("Performing task: " + task);
                    Thread.Sleep(1000);  // simulate work...
                }
                else
                {
                    _wh.WaitOne();  // No more tasks - wait for a signal
                }
            }
        }

        #region IDisposable Members

        public void Dispose()
        {
            EnqueueTask(null);
            _worker.Join();
            _wh.Close();
        }

        #endregion
    }
}
